﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace Elca.MvvmHelpers {

    /// <summary>
    /// Allows setting the focus on a <c>FrameworkElement</c>.
    /// Inspired by http://stackoverflow.com/questions/1356045/set-focus-on-textbox-in-wpf-from-view-model-c-wpf
    /// </summary>
    public class FocusHelper {

        private readonly static List<FrameworkElement> s_knownElements = new List<FrameworkElement>();

        public static readonly DependencyProperty IsFocusedProperty =
                DependencyProperty.RegisterAttached(
                        "IsFocused", typeof (bool), typeof (FocusHelper),
                        new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));


        public static bool GetIsFocused(FrameworkElement obj) {
            return (bool) obj.GetValue(IsFocusedProperty);
        }

        public static void SetIsFocused(FrameworkElement obj, bool value) {
            obj.SetValue(IsFocusedProperty, value);
        }

        private static void OnIsFocusedPropertyChanged(DependencyObject d,
                                                       DependencyPropertyChangedEventArgs e) {
            var fe = (FrameworkElement) d;

            if (! s_knownElements.Contains(fe)) {
                HookHandlers(fe);
                s_knownElements.Add(fe);
            }

            if ((bool) e.NewValue) {

                fe.Focus(); // Don't care about false values.
            }
        }

        private static void HookHandlers(FrameworkElement fe) {
            fe.Unloaded += OnUnloaded;
            fe.LostFocus += OnLostFocus;
        }

        private static void OnLostFocus(object sender, RoutedEventArgs e) {

            FrameworkElement fe = (FrameworkElement) sender;

            SetIsFocused(fe, false);
        }

        private static void OnUnloaded(object sender, RoutedEventArgs e) {
            FrameworkElement fe = (FrameworkElement)sender;
            UnhookHandlers(fe);
            s_knownElements.Remove(fe);
        }

        private static void UnhookHandlers(FrameworkElement fe) {
            fe.Unloaded -= OnUnloaded;
            fe.LostFocus -= OnLostFocus;
        }
    }
}
